Coverage Report

Created: 2026-02-05 09:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
D:\a\scloud-dns\scloud-dns\src\dns\packet\answer\mod.rs
Line
Count
Source
1
use crate::dns::q_class::DNSClass;
2
use crate::dns::q_name::parse_qname;
3
use crate::dns::q_type::DNSRecordType;
4
use crate::exceptions::SCloudException;
5
6
#[derive(Debug, PartialEq)]
7
pub struct AnswerSection {
8
    pub q_name: String,
9
    pub r_type: DNSRecordType,
10
    pub r_class: DNSClass,
11
    pub ttl: u32,
12
    pub rdlength: u16,
13
    pub rdata: Vec<u8>,
14
}
15
16
impl AnswerSection {
17
    /// Serialize AnswerSection into bytes (simple form, no compression)
18
    ///
19
    /// # Exemple :
20
    /// ```
21
    /// use crate::dns::answer::AnswerSection;
22
    /// use crate::dns::q_type::DNSRecordType;
23
    /// use crate::dns::q_class::DNSClass;
24
    ///
25
    /// // A Answer for example.com → 93.184.216.34
26
    /// let answer = AnswerSection {
27
    ///     q_name: "example.com".to_string(),
28
    ///     r_type: DNSRecordType::A,
29
    ///     r_class: DNSClass::IN,
30
    ///     ttl: 300,
31
    ///     rdlength: 4,
32
    ///     rdata: vec![93, 184, 216, 34],
33
    /// };
34
    ///
35
    /// let bytes = answer.to_bytes().unwrap();
36
    ///
37
    /// // NAME + TYPE + CLASS + TTL + RDLENGTH + RDATA
38
    /// assert!(bytes.len() > 12);
39
    /// ```
40
3
    pub fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> {
41
3
        let mut buf = Vec::new();
42
43
        // Encode NAME
44
5
        for label in 
self.q_name.split('.')3
{
45
5
            let len = label.len();
46
5
            if len > 63 {
47
2
                return Err(SCloudException::SCLOUD_ANSWER_DESERIALIZATION_FAILED_LABEL_TOO_LONG);
48
3
            }
49
3
            buf.push(len as u8);
50
3
            buf.extend_from_slice(label.as_bytes());
51
        }
52
1
        buf.push(0x00);
53
54
1
        let rtype_u16 =
55
1
            u16::try_from(self.r_type).expect("Cannot convert AnswerSection q_type to u16");
56
1
        buf.extend_from_slice(&rtype_u16.to_be_bytes());
57
58
1
        let rclass_u16 = u16::try_from(self.r_class).unwrap();
59
1
        buf.extend_from_slice(&rclass_u16.to_be_bytes());
60
61
1
        buf.extend_from_slice(&self.ttl.to_be_bytes());
62
1
        buf.extend_from_slice(&self.rdlength.to_be_bytes());
63
1
        buf.extend_from_slice(&self.rdata);
64
65
1
        Ok(buf)
66
3
    }
67
68
    /// Deserialize one AnswerSection and return (section, consumed_bytes)
69
    ///
70
    /// # Exemple :
71
    /// ```
72
    /// use crate::dns::answer::AnswerSection;
73
    /// use crate::dns::q_type::DNSRecordType;
74
    /// use crate::dns::q_class::DNSClass;
75
    ///
76
    /// // example.com A 93.184.216.34
77
    /// let raw_answer: Vec<u8> = vec![
78
    ///     0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e',
79
    ///     0x03, b'c', b'o', b'm',
80
    ///     0x00,             // End of NAME
81
    ///     0x00, 0x01,       // TYPE = A
82
    ///     0x00, 0x01,       // CLASS = IN
83
    ///     0x00, 0x00, 0x01, 0x2c, // TTL = 300
84
    ///     0x00, 0x04,       // RDLENGTH = 4
85
    ///     93, 184, 216, 34, // RDATA
86
    /// ];
87
    ///
88
    /// let (answer, consumed) = AnswerSection::from_bytes(&raw_answer, 0).unwrap();
89
    ///
90
    /// assert_eq!(answer.q_name, "example.com");
91
    /// assert_eq!(answer.r_type, DNSRecordType::A);
92
    /// assert_eq!(answer.r_class, DNSClass::IN);
93
    /// assert_eq!(answer.ttl, 300);
94
    /// assert_eq!(answer.rdata, vec![93, 184, 216, 34]);
95
    /// assert_eq!(consumed, raw_answer.len());
96
    /// ```
97
3
    pub(crate) fn from_bytes(
98
3
        buf: &[u8],
99
3
        offset: usize,
100
3
    ) -> Result<(AnswerSection, usize), SCloudException> {
101
3
        let (q_name, mut pos) = parse_qname(buf, offset).unwrap();
102
103
3
        if pos + 10 > buf.len() {
104
1
            return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_ANSWER_HEADER_TOO_SHORT);
105
2
        }
106
107
2
        let r_type = DNSRecordType::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap();
108
2
        pos += 2;
109
110
2
        let r_class = DNSClass::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap();
111
2
        pos += 2;
112
113
2
        let ttl = u32::from_be_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]);
114
2
        pos += 4;
115
116
2
        let rdlength = u16::from_be_bytes([buf[pos], buf[pos + 1]]);
117
2
        pos += 2;
118
119
2
        if pos + rdlength as usize > buf.len() {
120
1
            return Err(SCloudException::SCLOUD_IMPOSSIBLE_PARSE_ANSWER_RDATA_OUT_OF_BOUNDS);
121
1
        }
122
1
        let rdata = buf[pos..pos + rdlength as usize].to_vec();
123
1
        pos += rdlength as usize;
124
125
1
        Ok((
126
1
            AnswerSection {
127
1
                q_name,
128
1
                r_type,
129
1
                r_class,
130
1
                ttl,
131
1
                rdlength,
132
1
                rdata,
133
1
            },
134
1
            pos - offset,
135
1
        ))
136
3
    }
137
}